package com.lcy.struct.bTreePlus.disk.recovery.redo;

import com.lcy.struct.bTreePlus.disk.constants.BPlusPublicConstants;
import com.lcy.struct.bTreePlus.disk.file.FileUtil;
import com.lcy.struct.bTreePlus.disk.recovery.LSN;
import com.lcy.struct.bTreePlus.disk.recovery.RecoveryCenter;
import com.lcy.struct.bTreePlus.utils.BplusUtils;
import lombok.SneakyThrows;

import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;

/**
 * @author lucongyang
 * @date Created in  10:53
 * @description
 */
class WriteTransactionIntoRedo {
    /**
     * 每次redo的数据结构：
     *
     * @param toWriteBytes  要写入的数据
     * @param transactionId 事务id（不超过20位且全局唯一）
     * @see BPlusPublicConstants.REDO_START_STR
     * @see BPlusPublicConstants.REDO_CONTENT + content
     * ...
     * @see BPlusPublicConstants.REDO_CONTENT + content
     * @see BPlusPublicConstants.REDO_END_STR + @see BPlusPublicConstants.REDO_END_COMMIT_STR (如果没有执行完则为空)
     * 每一页的大小为
     * @see BPlusPublicConstants.REDO_PAGE_SIZE
     * @see BPlusPublicConstants.REDO_END_COMMIT_STR 为redo已全部执行完的标志
     */
    void writeIntoRedo(byte[] toWriteBytes, long transactionId) {
        // 每页存数据的长度
        byte[] redoContentBytes = BPlusPublicConstants.REDO_CONTENT.getBytes();
        int size = BPlusPublicConstants.REDO_PAGE_SIZE - redoContentBytes.length;
        assert size > 0;
        int contentLength = ((int) Math.ceil(toWriteBytes.length / (size + 0.))) * BPlusPublicConstants.REDO_PAGE_SIZE;
        byte[] desBytes = new byte[BPlusPublicConstants.REDO_PAGE_SIZE + contentLength + BPlusPublicConstants.REDO_PAGE_SIZE];
        BplusUtils.initByteArray(desBytes);
        BplusUtils.replaceArr(desBytes, (BPlusPublicConstants.REDO_START_STR + transactionId).getBytes(), 0);
        BplusUtils.replaceArr(desBytes, BPlusPublicConstants.REDO_END_STR.getBytes(), desBytes.length - BPlusPublicConstants.REDO_PAGE_SIZE);
        for (int i = 0; i * size < toWriteBytes.length; i++) {
            byte[] bytes = Arrays.copyOfRange(toWriteBytes, i * size, (i + 1) * size);
            BplusUtils.replaceArr(desBytes, redoContentBytes, BPlusPublicConstants.REDO_PAGE_SIZE + i * BPlusPublicConstants.REDO_PAGE_SIZE);
            BplusUtils.replaceArr(desBytes, bytes, BPlusPublicConstants.REDO_PAGE_SIZE + i * BPlusPublicConstants.REDO_PAGE_SIZE + redoContentBytes.length);
        }
        long totalLength = desBytes.length;

        FileChannel channel = RedoCenter.getInstance().getRedoChannel();
        RedoCenter.REDO_END_POSITION_WRITE_LOCK.lock();
        try {
            long endPosition = RedoCenter.getInstance().getRedoEndPosition();
            FileUtil.getInstance().straightRewrite(channel, endPosition, totalLength, true, desBytes);
            // 写末位置指针
            RedoCenter.getInstance().allocate(totalLength);
        } finally {
            RedoCenter.REDO_END_POSITION_WRITE_LOCK.unlock();
        }
    }

    static WriteTransactionIntoRedo getInstance() {
        return Singleton.INSTANCE;
    }

    private static class Singleton {
        private static final WriteTransactionIntoRedo INSTANCE = new WriteTransactionIntoRedo();
    }

    private WriteTransactionIntoRedo() {
    }
}
